/* 
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

Project: MyMANET - A Platform to Build Customized MANET's
Developers: Ashwin Paranjpe, Santosh Vempala
Georgia Institute of Technology, Atlanta, USA
*/

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<stdint.h>	//for uint8_t and uint32_t
#include<sys/socket.h>
#include<features.h>
#include<linux/if_packet.h>
#include<linux/if_ether.h>
#include<errno.h>
#include<sys/ioctl.h>
#include<net/if.h>
#include<time.h>
#include"sniffer.h"

#define MANIFOLD_UPDATE_TYPE 0x3434
#define BUFFER_LEN 512
#define MAX_NUM_OF_HOSTS 20
#define MAX_NUM_OF_STAT_ENTRIES 20
#define DEBUG 0
#define NUM_OF_REAL_HOSTS 6
#define ACTIVE_TIMEOUT 20

uint8_t g_broadcast_mac[6]={0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
static uint8_t g_mac[6];
static int g_my_id = -1;
int (*g_pluggable_func_ptr)(int receiver, unsigned char recvmac[6], int s, int r) = NULL;

extern int manifold_routing(int, unsigned char *, int, int);
int g_tolerance = 5;
int g_is_emb_dev = 0;	/*1: Is an embedded device. 0: Is NOT an embedded device.*/


uint8_t g_distance_table[MAX_NUM_OF_HOSTS]={0};

struct global_stats{	/*Structure to hold the link statistics*/
	int32_t sent;
	int32_t recv;
	time_t timestamp;		
};
struct global_stats g_stats[MAX_NUM_OF_HOSTS][MAX_NUM_OF_HOSTS];

time_t current_time;



struct stat_list{
	unsigned char mac[6];
	uint32_t packets;
	uint32_t session;
} __attribute__((packed));

struct host{
	unsigned char hostmac[6];
	uint32_t ip_long;
	char node_name[10];
	uint32_t tx_pkts;
	uint32_t tx_session;
	struct stat_list statlist[MAX_NUM_OF_STAT_ENTRIES];
	uint32_t statlist_count;
}hostlist[MAX_NUM_OF_HOSTS];

int g_host_count = 0;



struct stat_list *tmpstatlist;



int CreateRawSocket(int protocol_to_sniff)
{
	int rawsock;

	if((rawsock = socket(PF_PACKET, SOCK_RAW, htons(protocol_to_sniff)))== -1)
	{
		perror("Error creating raw socket: ");
		exit(-1);
	}

	return rawsock;
}

int BindRawSocketToInterface(char *device, int rawsock, int protocol)
{

	struct sockaddr_ll sll;
	struct ifreq ifr;

	bzero(&sll, sizeof(sll));
	bzero(&ifr, sizeof(ifr));

	/*Copy device name*/
	strncpy((char *)ifr.ifr_name, device, IFNAMSIZ);

	/*Get hardware address */
	if (ioctl(rawsock, SIOCGIFHWADDR, &ifr) < 0)
	{
		printf("ioctl Error: SIOCGIFHWADDR");
		exit(1);
	}

	memset(g_mac, 0, 6);
	memcpy(g_mac, ifr.ifr_hwaddr.sa_data, 6);
#if DEBUG
	printf("\nMy Mac is : %x:%x:%x:%x:%x:%x\n", g_mac[0], g_mac[1], g_mac[2], g_mac[3], g_mac[4], g_mac[5]);
#endif

	/*Get the Interface Index  */
	if((ioctl(rawsock, SIOCGIFINDEX, &ifr)) == -1)
	{
		printf("Error getting Interface index !\n");
		exit(-1);
	}

	/*Bind Raw socket to this interface */
	sll.sll_family = AF_PACKET;
	sll.sll_ifindex = ifr.ifr_ifindex;
	sll.sll_protocol = htons(protocol); 


	if((bind(rawsock, (struct sockaddr *)&sll, sizeof(sll)))== -1)
	{
		perror("Error binding raw socket to interface\n");
		exit(-1);
	}

	return 1;

}

void PrintPacketInHex(unsigned char *packet, int len)
{
	unsigned char *p = packet;

	printf("\n\n---------Packet---Starts----\n\n");

	while(len--)
	{
		printf("%.2x ", *p);
		p++;
	}

	printf("\n\n--------Packet---Ends-----\n\n");

}


PrintInHex(const char *mesg, unsigned char *p, int len)
{
	printf("%s", mesg);

	while(len--)
	{
		printf("%.2X ", *p);
		p++;
	}

}

int check_and_add_new_host(unsigned char srcmac[6], uint32_t ip_long, char node_name[10], uint32_t tx_pkts, uint32_t tx_session)
{

	int i=0, found=0;
	char *ip_address, *ip, *ret;
	char ret_name[30], ret_ip[20], ip_add[20], s[100];
	int name_len, ip_len;
	FILE *stream;
        FILE *host_stream;

        if((host_stream = fopen("./../../config/hosts", "w")) == NULL){
                printf("\nError: log file could not be opened for appending.\n");
                return;
        }

        for (i=0; i<g_host_count; i++) {
                memset (&s, '\0', 100);
                sprintf (s, "%s %s %x:%x:%x:%x:%x:%x %x:%x:%x:%x:%x:%x\n", hostlist[i].node_name, inet_ntoa(hostlist[i].ip_long), hostlist[i].hostmac[0], hostlist[i].hostmac[1], hostlist[i].hostmac[2], hostlist[i].hostmac[3], hostlist[i].hostmac[4], hostlist[i].hostmac[5], g_mac[0], g_mac[1], g_mac[2], g_mac[3], g_mac[4], g_mac[5]);
                fprintf (host_stream, s);
        }
        fclose (host_stream);

        i=0;
	while(i < g_host_count){
		if(memcmp(srcmac, hostlist[i].hostmac, 6) == 0){
			
			memcpy(hostlist[i].hostmac, srcmac, 6);
			hostlist[i].ip_long = ip_long;
			memcpy(hostlist[i].node_name, node_name, 10);
			hostlist[i].tx_pkts = tx_pkts;
			hostlist[i].tx_session = tx_session;
			found = 1;
#if DEBUG
			printf("found host: %ld : %s \n", ip_long, node_name);
			fflush (stdout);
#endif
			return 0;
		}
		i++;
	}

	if((i == g_host_count) && (found == 0) && (g_host_count < MAX_NUM_OF_HOSTS)){
		/*Before adding the new host, check whether this is a duplicate IP address*/
		i = 0;
		while(i < g_host_count){
			if(hostlist[i].ip_long == ip_long){
				/*This host has a duplicate IP w.r.t. a node in MyMANET!. Do not add it.*/

				/*Check whether this node is actually participating in an IP address conflict*/
				if((memcmp(g_mac, srcmac, 6) == 0)){
					printf("\nNotification: IP ADDRESS CONFLICT DETECTED.\nPlease change IP and try again.\n");
					#if 0
					chdir("../../scripts");
					char shell_comm[100];
					memset (&shell_comm, '\0', 100);
					sprintf (shell_comm, "bash regenerate_ip.sh %ld", ip_long);
					system(shell_comm);
					chdir("/tools/raw");
					#endif
					exit(1);
				}
				return -1;
			}
			i++;
		}
		/*Did not find the host in the list. It is a new host and will be added.*/
		memcpy(hostlist[g_host_count].hostmac, srcmac, 6);
		hostlist[g_host_count].ip_long = ip_long;
		memcpy(hostlist[g_host_count].node_name, node_name, 10);
		hostlist[g_host_count].tx_pkts = tx_pkts;
		hostlist[g_host_count].tx_session = tx_session;
		g_host_count++;
#if DEBUG
		printf("added host: %ld : %s \n", ip_long, node_name);
#endif
		return 0;
	}
	else if(g_host_count >= MAX_NUM_OF_HOSTS){
		printf("\nHost List is FULL\nCannot add more.\n");
		return -1;
	}

}

void check_and_add_new_statentry(unsigned char srcmac[6], struct stat_list *tmpstatlist)
{
	int i=0, found=0, hostid = 0;

	while(i < g_host_count){

		if(memcmp(srcmac, hostlist[i].hostmac, 6) == 0){
			hostid = i;
			found = 1;
			break;
		}

		i++;
	}

	if(found == 0)
	{	
		printf("ENTRY does not exist: Something Wrong! Entry should exist!\n");
		return;		/*Something wrong. Entry must exist!*/
	}

	found = 0;
	i=0;
	while(i < hostlist[hostid].statlist_count){

		if(memcmp(tmpstatlist->mac, hostlist[hostid].statlist[i].mac, 6) == 0){
			memcpy(&hostlist[hostid].statlist[i], tmpstatlist, sizeof(struct stat_list));
			found = 1;
#if DEBUG
			printf("found mac %x = %x\n", tmpstatlist->mac[5], hostlist[hostid].statlist[i].mac[5]);
#endif
			break;
		}
		i++;
	}

	if((i == hostlist[hostid].statlist_count) && (found == 0) && (hostlist[hostid].statlist_count < MAX_NUM_OF_STAT_ENTRIES)){
		memcpy(&hostlist[hostid].statlist[(hostlist[hostid].statlist_count)], tmpstatlist, sizeof(struct stat_list));
		hostlist[hostid].statlist_count++;
#if DEBUG
		printf("added mac\n");
#endif
	}
	else if((hostlist[hostid].statlist_count) >= MAX_NUM_OF_STAT_ENTRIES){
		printf("\nStat List is FULL\nCannot add more.\n");
	}

}

int get_id_from_mac(uint8_t mac[6])
{
	int i=0;
	while( i < g_host_count){
		if(memcmp(mac, hostlist[i].hostmac, 6) == 0)
			return i;
		i++;
	}
	return -1;
}

void update_link_stats(uint32_t sender, uint32_t receiver, uint32_t sent, uint32_t recv)
{
	if(sender>=0 && sender<g_host_count && receiver>=0 && receiver<g_host_count){
		g_stats[sender][receiver].sent = sent;
		g_stats[sender][receiver].recv = recv;
		g_stats[sender][receiver].timestamp = time(NULL);
	}
}

void print_distances_from_me()
{
	int receiver=0;

	printf("\n\n\n\n========= Distance Table ==========\n");
	printf("\n%-20s", "CURRENT DISTANCES");
		
	while(receiver < g_host_count){
		printf("%-20s", hostlist[receiver].node_name);
		receiver++;
	}
	receiver = 0;
	printf("\n%-20s","FROM THIS HOST");
	while(receiver < g_host_count){
		printf("%-20d", g_distance_table[receiver]);
		receiver++;
	}
	printf("\n");

}

void print_stats_in_file(){
	FILE *fp=NULL;
	int sender=0, receiver=0;
	double time_difference=0;

	if((fp = fopen("./java/statlist_web", "w")) == NULL){
		printf("\nError: statlist_web file could not be opened.\n");
		return;
	}

	while(receiver < g_host_count){
		fprintf(fp, "%s", hostlist[receiver].node_name);
		receiver++;
		if(receiver != g_host_count){
			fprintf(fp, " ");
		}
	}
	while(sender < g_host_count){
		fprintf(fp, "\n%s", hostlist[sender].node_name);
		receiver = 0;
		while(receiver < g_host_count){

			current_time = time(NULL);
			time_difference = difftime(current_time, g_stats[sender][receiver].timestamp);

			/*Check whether record has valid value and is NOT stale.*/
			if((g_stats[sender][receiver].sent >= 0) && (time_difference < ACTIVE_TIMEOUT)){
				fprintf(fp, " %d,%d", g_stats[sender][receiver].sent, g_stats[sender][receiver].recv);	
			}
			else{
				fprintf(fp, " %s","--");
			}
			receiver++;
		}
		sender++;

	}
	fclose(fp);
}

void print_real_link_stats_table(){
	int sender=0, receiver=0;
	double time_difference=0;

	system("clear");
	printf("\nNumber of MyMANET nodes detected: %d\n\n", g_host_count);
	printf("\n===== Global Statistics Table =====\n\n\n");
	printf("%-20s", "SENDER / RECEIVER");
	
	while(receiver < g_host_count){
		printf("%-20s", hostlist[receiver].node_name);
		receiver++;
	}

	printf("\n");
	while(sender < g_host_count){
		printf("\n%-20s", hostlist[sender].node_name);
		receiver = 0;

		while(receiver < g_host_count){
			current_time = time(NULL);
			time_difference = difftime(current_time, g_stats[sender][receiver].timestamp);

			/*Check whether record has valid value and is NOT stale.*/
			if((g_stats[sender][receiver].sent >= 0) && (time_difference < ACTIVE_TIMEOUT)){
				printf("%3d, %-15d", g_stats[sender][receiver].sent, g_stats[sender][receiver].recv);	
			}
			else{
				printf("%-20s","--");
			}
			receiver++;
		}
		printf("\n");
		sender++;

	}
	print_distances_from_me();
}

void calculate_and_print_link_stats_table()
{
	uint32_t receiver_count=0, host_count=0, sender_count=0, print_sender=0, print_receiver=0; 
	uint32_t print_percent_loss=-1;

	/*Iterate through the Senders*/
	while(sender_count < g_host_count){

		/*Iterate through the entire hostlist*/
		host_count=0;
		while(host_count < g_host_count){

			receiver_count=0;
			while(receiver_count < hostlist[host_count].statlist_count){

				if( memcmp(hostlist[host_count].statlist[receiver_count].mac, hostlist[sender_count].hostmac, 6) == 0){
					/*This host has received packets from this Sender.*/
					print_sender = get_id_from_mac(hostlist[sender_count].hostmac);
					print_receiver = get_id_from_mac(hostlist[host_count].hostmac);
					if(print_sender<0 || print_receiver<0){
						break;
					}
					else if(hostlist[sender_count].tx_session == hostlist[host_count].statlist[receiver_count].session){

						/*EXTRA CODE*/

						/*Calculate percentage loss, then print it.*/
						print_percent_loss = (hostlist[sender_count].tx_pkts - hostlist[host_count].statlist[receiver_count].packets);
						/*Now that we are calculating end to end link statistics, 
						  it might happen that a node receives duplicate packets from multiple 
						  forwarding intermediate nodes. So, it can happen that number of packets received are much greater
						  than the number of pkts sent by the original source.
						  Hence, if the packet loss difference is negative, set it to zero.
						  i.e. logically, that means that there's no packet loss.*/
						if(print_percent_loss < 0){
							print_percent_loss = 0;
						}

						/*Never divide by zero. check this condition. No packet sent, no question of pkt loss.*/
						if(hostlist[sender_count].tx_pkts == 0)
							print_percent_loss = 0;
						else
							print_percent_loss = ((print_percent_loss * 100) /  hostlist[sender_count].tx_pkts);

						/*NOTE:
						  Currently we are not using/printing the percent loss.
						  So, the print_percent_loss field is not used further.*/

						/*END OF EXTRA CODE*/

						/*Update the statistics for this link*/	
						update_link_stats(print_sender, print_receiver, hostlist[sender_count].tx_pkts, 
							hostlist[host_count].statlist[receiver_count].packets);

						/*If this update is specific to me, then update distances from me.*/
						/*Get this nodes ID (self-ID), if it has not been fetched yet.*/
						if(g_my_id == -1){
							g_my_id = get_id_from_mac(g_mac);
							/*Set the distance to this node, itself as 0*/
							g_distance_table[g_my_id] = 0;
						}

						if(print_sender == g_my_id){
							(*g_pluggable_func_ptr)(print_receiver, hostlist[host_count].hostmac,
							hostlist[sender_count].tx_pkts, hostlist[host_count].statlist[receiver_count].packets);
						}

					}
					break;

				}
				receiver_count++;			
			}
			host_count++;
		}
		sender_count++;
	}

	/*This is where the actual printing happens. The following function is also called periodically from the sniffer section.*/
	if(!g_is_emb_dev){
		print_real_link_stats_table();
		print_stats_in_file();
	}
}	

void write_refined_stats()
{
	int receiver_count=0, host_count=0, sender_count=0;
	FILE *fp = NULL;
	time_t mytime = time(0);

	if((fp = fopen("./statlist_refined", "a")) == NULL){
		printf("\nError: statlist_refined file could not be opened.\n");
		return;
	}

	sender_count=0;
	while(sender_count < g_host_count){

		mytime = time(0);

		/*Iterate through the Senders*/
		fprintf(fp, "\n\n %s SENDER : %x:%x:%x:%x:%x:%x Sent %d in Session %d", asctime(localtime(&mytime)), hostlist[sender_count].hostmac[0], hostlist[sender_count].hostmac[1], hostlist[sender_count].hostmac[2], hostlist[sender_count].hostmac[3], hostlist[sender_count].hostmac[4], hostlist[sender_count].hostmac[5], hostlist[sender_count].tx_pkts, hostlist[sender_count].tx_session);

		/*Iterate through the entire hostlist*/
		host_count=0;
		while(host_count < g_host_count){

			receiver_count=0;
			while(receiver_count < hostlist[host_count].statlist_count){

				if( memcmp(hostlist[host_count].statlist[receiver_count].mac, hostlist[sender_count].hostmac, 6) == 0){
					/*This host has received packets from this Sender.*/
					mytime = time(0);
					fprintf(fp, "\n %s Receiver : %x:%x:%x:%x:%x:%x Received %d belonging to Session %d", asctime(localtime(&mytime)), hostlist[host_count].hostmac[0], hostlist[host_count].hostmac[1], hostlist[host_count].hostmac[2], hostlist[host_count].hostmac[3], hostlist[host_count].hostmac[4], hostlist[host_count].hostmac[5], hostlist[host_count].statlist[receiver_count].packets, hostlist[host_count].statlist[receiver_count].session);
					break;

				}
				receiver_count++;			
			}
			host_count++;
		}
		sender_count++;
	}
	fclose(fp);
}	

ParseEthernetPacket(unsigned char *packet, int len, uint8_t am_i_a_gw)
{

	struct ethhdr *ethernet_header;
	uint32_t ip_long_nl=0;
	char node_name[10]="";
	uint32_t pkts_network = 0;
	uint32_t session_network = 0;
	uint32_t tx_pkts_network = 0;
	uint32_t tx_session_network = 0;
	uint8_t originator_mac[6]={0};
	int retval=0;
#if DEBUG
	FILE *fp = NULL;
	int count = 0, count2 = 0;
#endif

	int is_a_gateway=0;	/*This field indicates whether the current heart-beat packet has originated from a node that is a GW.*/
	int num_of_dns=0;
	int i=0;
	uint32_t ip_long_host=0;
	FILE *fp_dns = NULL;
					
	if( len < (sizeof(struct ethhdr) + (sizeof(uint8_t) * 6) + 10 + 1 + (sizeof(uint32_t) * 3)) ){
		/*This is the basic size of the packet. Actual size might be more than this value, depending on whether 
		this is a GW and the number of DNS IPs shared.
		*/
		printf("\nSniffer: Packet size too small ! Garbled packets observed.\n\n");
		return;
	}
	else {

		ethernet_header = (struct ethhdr *)packet;
#if DEBUG
		/* First set of 6 bytes are Destination MAC */
		PrintInHex("Destination MAC: ", ethernet_header->h_dest, 6);
		printf("\n");

		/* Second set of 6 bytes are Source MAC */
		PrintInHex("Source MAC: ", ethernet_header->h_source, 6);
		printf("\n");

		/* Last 2 bytes in the Ethernet header are the protocol it carries */
		PrintInHex("Protocol: ",(void *)&ethernet_header->h_proto, 2);
		printf("\n");
#endif

		if(ethernet_header->h_proto != MANIFOLD_UPDATE_TYPE){
#if DEBUG
			printf("\n\nNot our packet\n\n");
#endif
			return;
		}
		

		if(memcmp(ethernet_header->h_dest, g_broadcast_mac, 6) != 0) {
#if DEBUG
			printf("\n\nNot our packet\n\n");
#endif
			return;
		}

		packet = packet + sizeof(struct ethhdr);
		len -= sizeof(struct ethhdr);

		memcpy(originator_mac, packet, 6);

		packet = packet + 6;
		len -= 6;

		memcpy(&ip_long_nl, packet, sizeof(uint32_t));

		packet = packet + sizeof(uint32_t);
		len -= sizeof(uint32_t);

		memcpy(node_name, packet, 10);

		packet = packet + 10;
		len -= 10;

		memcpy(&is_a_gateway, packet, 1);
		packet = packet + 1;
		len -= 1;

		if(is_a_gateway && len){	/*Check whether originator of this heart-beat pkt is a GW.*/
			memcpy(&num_of_dns, packet, 1);
			packet = packet + 1;
			len -= 1;
			
			if(num_of_dns){

				/*Check whether the packet does hold all the remaining required fields.*/
				if(len < ((num_of_dns * sizeof(uint32_t)) + (2 * sizeof(uint32_t)))){
					printf("\nSniffer: Packet size too small ! Garbled packets observed.\n\n");
					return;
				}

				if(am_i_a_gw){		/*This machine is the GW. Don't touch the /etc/resolv.conf file.*/
					for(i=0;i<num_of_dns; i++){
						packet = packet + sizeof(uint32_t);
						len -= sizeof(uint32_t);
					}

				}
				else{
					if((fp_dns=fopen("/etc/resolv.conf", "w"))!=NULL){

						for(i=0;i<num_of_dns; i++){
							memcpy(&ip_long_nl, packet, sizeof(uint32_t));
							ip_long_host = ntohl(ip_long_nl);

							/*Write the IP address to the config file. Writes to file may block. 
							  Must be handled in a better way.#TODO*/
							char *ip_address_string = (char *)inet_ntoa(ip_long_host);
							if((strncmp(ip_address_string, "", 16))!=0)
								fprintf(fp_dns, "nameserver %s\n", (char *)ip_address_string);

							packet = packet + sizeof(uint32_t);
							len -= sizeof(uint32_t);
						}
						fclose(fp_dns);
					}
				}
			}
		}

		/*Check whether the packet does hold all the remaining required fields.*/
		if(len < (2 * sizeof(uint32_t))){
			printf("\nSniffer: Packet size too small ! Garbled packets observed.\n\n");
			return;
		}

		memcpy(&tx_pkts_network, packet, sizeof(uint32_t));

		packet = packet + sizeof(uint32_t);
		len -= sizeof(uint32_t);

		memcpy(&tx_session_network, packet, sizeof(uint32_t));

		packet = packet + sizeof(uint32_t);
		len -= sizeof(uint32_t);

		retval = check_and_add_new_host(originator_mac, ntohl(ip_long_nl), node_name, ntohl(tx_pkts_network), ntohl(tx_session_network));


		if(retval == -1){
			printf ("\nThis is an invalid host (might be having duplicate IP). Do not consider its statlist. Return.");
			return;
		}	

		while (len >= sizeof(struct stat_list)){

			tmpstatlist = (struct stat_list *)packet;
			pkts_network = tmpstatlist->packets;
			tmpstatlist->packets = ntohl(pkts_network);
			session_network = tmpstatlist->session;
			tmpstatlist->session = ntohl(session_network);

			check_and_add_new_statentry(originator_mac, tmpstatlist);

			packet = packet + sizeof(struct stat_list);
			len -= sizeof(struct stat_list);
		}

		/*Print the Stat-Matrix*/
		calculate_and_print_link_stats_table();

		/*START: Code for printing in statlist and statlist_refined files.*/
		if(!g_is_emb_dev)
			write_refined_stats();

#if DEBUG
		if((fp = fopen("./statlist", "a")) == NULL){
			printf("\nError: statlist file does not exist. Could not write.\n");
			return;
		}
		else{
			printf("\n\nThe Stat List is as follows:\n");
		}
#endif

		/*Code to write to a file*/
#if DEBUG
		fprintf(fp, "\n==========================\n");
#endif

#if DEBUG
		while(count2 < g_host_count){

			PrintInHex("HOST MAC: ", (hostlist[count2]).hostmac, 6);
			printf("\n");

			/*Code to write to a file*/
			fprintf(fp, "\n\nHOST: %x:%x:%x:%x:%x:%x TX pkts: %d TX session: %d\n", hostlist[count2].hostmac[0], 
					hostlist[count2].hostmac[1], hostlist[count2].hostmac[2], hostlist[count2].hostmac[3], 
					hostlist[count2].hostmac[4], hostlist[count2].hostmac[5], hostlist[count2].tx_pkts, 
					hostlist[count2].tx_session);
			/*END*/

			count = 0;
			while(count < hostlist[count2].statlist_count){

				PrintInHex("MAC: ", hostlist[count2].statlist[count].mac, 6);
				PrintInHex("PACKETS: ", (void *) &hostlist[count2].statlist[count].packets, sizeof(uint32_t));
				PrintInHex("SESSION: ", (void *) &hostlist[count2].statlist[count].session, sizeof(uint32_t));
				printf("\npackets :%d\n", hostlist[count2].statlist[count].packets);
				printf("session :%d\n", hostlist[count2].statlist[count].session);
				printf("\n");

				/*Code to write to a file*/
				fprintf(fp, "%x:%x:%x:%x:%x:%x", hostlist[count2].statlist[count].mac[0], hostlist[count2].statlist[count].mac[1], hostlist[count2].statlist[count].mac[2], hostlist[count2].statlist[count].mac[3], hostlist[count2].statlist[count].mac[4], hostlist[count2].statlist[count].mac[5]);
				fprintf(fp, " %d", hostlist[count2].statlist[count].packets);
				fprintf(fp, " %d\n", hostlist[count2].statlist[count].session);
				/*END*/

				printf("I am here\n\n");

				count++;

			}
			count2++;
		}	
#endif
#if DEBUG
		if(fp != NULL){
			fclose(fp);
		}
#endif

		/*END: Code for printing in statlist and statlist_refined files.*/

	}
}

void initialize_timestamps()
{
	int i=0, j=0;
	for(i=0; i < MAX_NUM_OF_HOSTS; i++)
		for(j=0; j < MAX_NUM_OF_HOSTS; j++)
			g_stats[i][j].timestamp = time(NULL);
}

void initialize_distances()
{
	int i=0;
	for(i=0; i < MAX_NUM_OF_HOSTS; i++){
		g_distance_table[i] = 255;
	}
}
/*argv[1] is the interface*/
/*argv[2] is the TOLERANCE parameter for kernel-space distance table updates.*/
/*argv[3] is the flag to indicate whether this node is a GW*/
/*argv[4] is the flag to indicate whether this is an embedded router having several constraints. 
This is used for certain optimizations to conserve power and memory. */

main(int argc, char **argv)
{
	int raw;
	unsigned char packet_buffer[BUFFER_LEN]; 
	int len;
	struct sockaddr_ll packet_info;
	int packet_info_size = sizeof(packet_info);
	struct timeval timeout;
	int retval=0;


	if(argc != 5){
		printf("\nusage: sudo ./sniff [INTERFACE] [TOLERANCE] [IS_GW] [IS_EMB_DEV]\n\n");
		exit(0);
	}

	/*Clear the percentage loss table and set values to -1*/
	memset(g_stats, -1, (sizeof(struct global_stats) * MAX_NUM_OF_HOSTS * MAX_NUM_OF_HOSTS));

	/*Initialize Time-Stamps*/
	initialize_timestamps();

	/*Initialize Distance Table*/
	initialize_distances();

	/*Create Raw socket */
	raw = CreateRawSocket(MANIFOLD_UPDATE_TYPE);


	/*Bind socket to interface */
	BindRawSocketToInterface(argv[1], raw, MANIFOLD_UPDATE_TYPE);


	/*Get the tolerance parameter*/
	g_tolerance = atoi(argv[2]);
	printf("\nTolerance is : %d\n", g_tolerance);

	/*Get the optimization parameter for embedded devices*/
	g_is_emb_dev = atoi(argv[4]);
	if(g_is_emb_dev)
		printf("\nThis is a memory and CPU constrained embedded device. Optimizations switched ON.\n");
	else 
		printf("\nThis is NOT a memory and CPU constrained embedded device. Optimizations switched OFF.\n");

	/*Initialize the pluggable routing function.*/
	g_pluggable_func_ptr = &manifold_routing;

	/*Start Sniffing*/
	while(1)
	{

		len = 0;
		retval=0;
		memset(packet_buffer, 0, BUFFER_LEN);
		timeout.tv_sec = 0;
		timeout.tv_usec = 0;
		fd_set fds;
		FD_ZERO(&fds);
		FD_SET(raw, &fds);

		retval = select((raw+1), &fds, 0, 0, &timeout);

		if(retval){
			len = recvfrom(raw, packet_buffer, BUFFER_LEN, 0, (struct sockaddr*)&packet_info, &packet_info_size);
		}
		else if(retval == -1){
			perror("select():");
		}
		else{
			if(!g_is_emb_dev){
				sleep(1);
				print_real_link_stats_table();
				print_stats_in_file();
			}
		}

		if(len == -1)
		{

			perror("Recv from returned -1: ");
			exit(-1);
		}
		else if(len > 0)
		{


#if DEBUG	
			/*A Packet has been received*/
			PrintPacketInHex(packet_buffer, len);
#endif

			/*Parse Ethernet Packet*/
			ParseEthernetPacket(packet_buffer, len, atoi(argv[3]));
		}
	}


	return 0;
}
